﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using gt.dotnetcore.webapi.DataAccess;
using gt.dotnetcore.webapi.DataAccess.Impl;
using gt.dotnetcore.webapi.Model.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using NLog.Extensions.Logging;
using gt.dotnetcore.webapi.extension;
using gt.dotnetcore.webapi.extension.Middlewares;
using gt.AspNetCore.Authentication.Basic;
using gt.dotnetcore.webapi.extension.Filters;
using System.Security.Claims;
using Newtonsoft.Json;
using gt.dotnetcore.webapi.extension.Authorizations;
using gt.dotnetcore.webapi.extension.Services;
using Microsoft.AspNetCore.Authorization.Infrastructure;
using Microsoft.AspNetCore.Authorization;
using gt.dotnetcore.webapi.extension.Entity;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

namespace gt.dotnetcore.webapi
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        public IContainer ApplicationContainer { get; private set; }

        // This method gets called by the runtime. Use this method to add services to the container.
        //IServiceProvider
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(cfg =>
            {
                //var filter = ApplicationContainer.Resolve<DefaultExceptionFilterAttribute>();
                var filter = services.BuildServiceProvider().GetService<DefaultExceptionFilterAttribute>();
                if (filter != null)
                    cfg.Filters.Add(filter);
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            #region 注入

            services.AddSingleton<IBookDAL, MemoryBookDAL>();
            services.AddSingleton<IPermissionCheckService, TestPermissionCheckService>();
            services.AddTransient<DefaultExceptionFilterAttribute>();
            services.AddTransient<IAuthorizationHandler, PermissionCheckAuthorizationHandler>();
            services.AddTransient<GlobalApiLoggingMiddleware>();
            services.AddTransient<GlobalExceptionMiddleware>();

            //var serviceProvider = AutoFacRegister(services);

            #endregion

            #region 配置

            services.Configure<List<ClientEntity>>(Configuration.GetSection("Clients"));

            #endregion

            //jwt 认证
            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                var clients = Configuration.GetSection("Clients").Get<List<ClientEntity>>();
                options.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = "www.gt.com",
                    ValidAudiences = clients.Select(x => x.Appkey),
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("secret1secret1secret1secret1secret1"))
                };
                options.Events = new JwtBearerEvents
                {
                    OnTokenValidated = async (context) =>
                     {
                         var clams = new List<Claim> { new Claim(ClaimTypes.Email, "gt@163.com") }; 
                         context.Principal.AddIdentity(new ClaimsIdentity(clams, JwtBearerDefaults.AuthenticationScheme));
                         //context.Principal.Identity.Name
                         await Task.CompletedTask;
                     }
                };
            });

            //自定义auth认证
            //services.AddAuthentication(BasicDefault.AuthenticationScheme)
            //    .AddBasic(options =>
            //    {
            //        options.ValidateUser = (username, password) =>
            //        {
            //            var clients = Configuration.GetSection("Clients").Get<List<ClientEntity>>();
            //            if (clients == null || clients.Count == 0) return false;

            //            return clients.Exists(x => string.Equals(x.Appkey, username, StringComparison.CurrentCultureIgnoreCase)
            //                                                        && string.Equals(x.Appsecret, password, StringComparison.CurrentCultureIgnoreCase));
            //        };

            //        options.Events.OnTokenValidated = context =>
            //        {
            //            if (context.Principal.Identity.IsAuthenticated)
            //            {
            //                //var clients = Configuration.GetSection("Clients").Get<List<ClientOptions>>();
            //                //if (clients == null || clients.Count == 0) return Task.CompletedTask;
            //                //var appkey = context.Principal.Identity.Name;
            //                //var actions = clients.Single(x => string.Equals(x.Appkey, appkey, StringComparison.CurrentCultureIgnoreCase)).Actions;
            //                //var c = new Claim(ClaimTypes.Actor, JsonConvert.SerializeObject(actions));
            //                //context.Principal.Claims.ToList().Add(c);
            //            }

            //            return Task.CompletedTask;
            //        };
            //    });

            services.AddAuthorization(options =>
            {
                options.AddPolicy("Permission", policy => policy.RequireClaim(ClaimTypes.Name));
            });

            //return serviceProvider;
        }

        // This method ge.ts called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
        {
            if (env.IsDevelopment())
            {
                //app.UseDeveloperExceptionPage();
            }
            else
            {
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                //app.UseHsts();
            }
            app.UseAuthentication();
            app.UseGlobalExceptionMiddleware();
            app.UseGlobalApiLoggingMiddleware();
            //app.UseHttpsRedirection();
            app.UseMvc();

            logger.LogInformation("gt.webapi start!");
        }

        public IServiceProvider AutoFacRegister(IServiceCollection services)
        {
            var builder = new ContainerBuilder();

            //very import
            //正常返回IServiceProvider 并不能替代原IServicePrivder。还需要自定义ServiceScopeFactory保证RequestServices返回的也是你自定义的ServiceProvider。
            //详细见https://www.cnblogs.com/artech/p/3rd-party-di-integration.html
            //此处autofac做了特殊处理
            builder.Populate(services);

            builder.RegisterType<MemoryBookDAL>().As<IBookDAL>().SingleInstance();
            builder.RegisterType<TestPermissionCheckService>().As<IPermissionCheckService>().SingleInstance();
            builder.RegisterType<PermissionCheckAuthorizationHandler>().As<IAuthorizationHandler>();
            builder.RegisterType<DefaultExceptionFilterAttribute>();

            builder.RegisterType<GlobalApiLoggingMiddleware>();
            builder.RegisterType<GlobalExceptionMiddleware>();

            ApplicationContainer = builder.Build();

            return new AutofacServiceProvider(ApplicationContainer);
        }
    }

    public class TestPermissionCheckService : extension.Services.IPermissionCheckService
    {
        private IConfiguration _configuration;
        public TestPermissionCheckService(IConfiguration configuration)
        {
            _configuration = configuration;
        }

        public bool Check(string appkey, string controllerAction)
        {
            var clients = _configuration.GetSection("Clients").Get<List<ClientEntity>>();
            if (clients == null || clients.Count == 0) return false;
            return clients.Exists(x => string.Equals(x.Appkey, appkey, StringComparison.CurrentCultureIgnoreCase)
                && x.AllowControllerActions.Exists(n => string.Equals(n, controllerAction, StringComparison.CurrentCultureIgnoreCase)));
        }
    }
}
